package cn.hg.solon.youcan.system.provider;

import java.util.List;
import java.util.Map;

import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.bean.copier.CopyOptions;
import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.text.CharSequenceUtil;
import org.dromara.hutool.core.text.StrValidator;
import org.dromara.hutool.core.util.ObjUtil;
import org.noear.solon.annotation.Component;
import org.noear.solon.data.annotation.Tran;

import com.easy.query.api4j.client.EasyQuery;
import com.easy.query.api4j.select.Queryable;
import com.easy.query.solon.annotation.Db;

import cn.hg.solon.youcan.common.constant.AppConstants;
import cn.hg.solon.youcan.common.constant.WebConstants;
import cn.hg.solon.youcan.common.enums.BeanStatus;
import cn.hg.solon.youcan.common.exception.ServiceException;
import cn.hg.solon.youcan.system.entity.EqPermission;
import cn.hg.solon.youcan.system.entity.EqRole;
import cn.hg.solon.youcan.system.entity.EqRolePermissionMapping;
import cn.hg.solon.youcan.system.entity.EqUserRoleMapping;
import cn.hg.solon.youcan.system.entity.Permission;
import cn.hg.solon.youcan.system.entity.Role;
import cn.hg.solon.youcan.system.entity.User;
import cn.hg.solon.youcan.system.service.PermissionService;

/**
 * @author 胡高
 */
@Component
public class EqPermissionProvider implements PermissionService {

    @Db("db1")
    private EasyQuery easyQuery;

    private Queryable<EqPermission> buildQuery(Map<String, Object> paraMap) {
        String word = (String)paraMap.get("word");
        String type = (String)paraMap.get("type");
        String sortField = (String)paraMap.get("sortField");
        String sortType = (String)paraMap.get("sortType");

        boolean customOrder = StrValidator.isNotBlank(sortField) && StrValidator.isNotBlank(sortType);

        // GROUP_CONCAT子查询
        Queryable<String> groupConcatQuery =
            // FROM sys_permission AS t2
            this.easyQuery.queryable(EqPermission.class)
            // WHERE t2.`type` = ${type}
            .where(w -> w.eq(StrValidator.isNotBlank(type), EqPermission::getType, type)
                // AND (t2.`name` LIKE '%${word}%' OR t2.`value` LIKE '%${word}%')
                .and(StrValidator.isNotBlank(word),
                    a -> a.like(EqPermission::getName, word)
                    .or().like(EqPermission::getValue, word)))
            // SELETE GROUP_CONCAT(t2.`ancestors`)
            .select(String.class, o -> o.sqlNativeSegment("GROUP_CONCAT({0})",
                c -> c.expression(EqPermission::getAncestors)));

        // FROM sys_permission AS t 
        return this.easyQuery.queryable(EqPermission.class)
            // WHERE (t.`type` = ${type} AND t.`name` LIKE '%${word}%' OR t.`value` LIKE '%${word}%'
            .where((d) -> d.and(StrValidator.isNotBlank(word) || StrValidator.isNotBlank(type),
                o -> o.eq(StrValidator.isNotBlank(type), EqPermission::getType, type)
                .or().like(StrValidator.isNotBlank(word), EqPermission::getName, word)
                .or().like(StrValidator.isNotBlank(word), EqPermission::getValue, word)
                // OR FIND_IN_SET(t2.`id`, ({GROUP_CONCAT子查询})))
                .or().sqlNativeSegment("FIND_IN_SET({0}, ({1}))",
                    c -> c.expression(EqPermission::getId).expression(groupConcatQuery))))
            // 排序字段
            .orderBy(customOrder, ob -> ob.sqlNativeSegment(CharSequenceUtil.toUnderlineCase(sortField)),
                !CharSequenceUtil.equalsIgnoreCase(sortType, WebConstants.ORDER_DIRECTION_DESC));
    }

    private Queryable<EqPermission> buildQueryByRole(Role role) {
        // FROM sys_permission AS t
        return this.easyQuery.queryable(EqPermission.class).distinct()
            // LEFT JOIN sys_role_permission AS t1 ON t1.`permission_id` = t.`id`
            .leftJoin(EqRolePermissionMapping.class,
                (p, rp) -> rp.eq(p, EqRolePermissionMapping::getPermissionId, EqPermission::getId))
            // WHERE t.`status` = 'ON' AND t1.`user_id` = ${user.id}
            .where((p, rp) -> rp.eq(EqRolePermissionMapping::getRoleId, role.getId()))
            // ORDER BY t.`sort` ASC
            .orderByAsc(o -> o.column(EqPermission::getSort));
    }

    private Queryable<EqPermission> buildQueryByUser(User user) {
        // FROM sys_permission AS t
        return this.easyQuery.queryable(EqPermission.class).distinct()
            // LEFT JOIN sys_role_permission AS t2 ON t2.`permission_id` = t.`id`
            .leftJoin(EqRolePermissionMapping.class,
                (p, rp) -> rp.eq(p, EqRolePermissionMapping::getPermissionId, EqPermission::getId))
            // LEFT JOIN sys_user_role AS t3 ON t3.`role_id` = t2.`role_id`
            .leftJoin(EqUserRoleMapping.class,
                (p, rp, ur) -> ur.eq(rp, EqUserRoleMapping::getRoleId, EqRolePermissionMapping::getRoleId))
            // WHERE t.`status` = 'ON' AND t3.`user_id` = ${user.id}
            .where((p, rp, ur) -> p.eq(EqPermission::getStatus, BeanStatus.ON.name())
                .then(ur.eq(EqUserRoleMapping::getUserId, user.getId())))
            // ORDER by t.sort ASC
            .orderByAsc(o -> o.column(EqPermission::getSort));
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.PermissionService#checkUnique(cn.hg.solon.youcan.system.entity.Permission)
     */
    @Override
    public boolean checkUnique(Permission bean) {
        return !this.easyQuery
            // FROM sys_permission AS t
            .queryable(EqPermission.class)
            // WHERE t.`parent_id` = ${bean.parentId}
            .where(w -> w.eq(EqPermission::getParentId, bean.getParentId())
                // t.`name` = ${bean.name}
                .eq(EqPermission::getName, bean.getName())
                // t.`id` <> ${bean.id}
                .ne(ObjUtil.isNotNull(bean.getId()), EqPermission::getId, bean.getId()))
            .any();
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.PermissionService#delete(java.lang.Integer)
     */
    @Tran
    @Override
    public boolean delete(Integer id) {
        /*
         * 检查是否子项
         */
        if (this.easyQuery.queryable(EqPermission.class).where(w -> w.eq(EqPermission::getParentId, id)).any()) {
            throw new ServiceException(AppConstants.RETURN_CODE_VALUE_FAILED, "存在子项，不允许删除！");
        }

        /*
         * 检查是否已分配于角色
         */
        if (// FROM sys_permission AS t
            this.easyQuery.queryable(EqPermission.class)
            // LEFT JOIN sys_role_permission_mapping AS t2 ON t1.`permission_id` = t.`id`
            .leftJoin(EqRolePermissionMapping.class, (d, rd) -> rd.eq(d, EqRolePermissionMapping::getPermissionId, EqPermission::getId))
            // LEFT JOIN sys_role AS t3 ON t3.`id` = t2.`role_id`
            .leftJoin(EqRole.class, (d, rd, r) -> r.eq(rd, EqRole::getId, EqRolePermissionMapping::getRoleId))
            // WHERE t2.`permission_id` = ${id}
            .where((d, rd,r) -> rd.eq(EqRolePermissionMapping::getPermissionId, id))
            .any()) {
            throw new ServiceException(AppConstants.RETURN_CODE_VALUE_FAILED, "已分配于角色，不允许删除！");
        }

        return this.easyQuery.deletable(EqPermission.class).whereById(id).executeRows() > 0L;
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.PermissionService#get(java.lang.Integer)
     */
    @Override
    public Permission get(Integer id) {
        return this.easyQuery.queryable(EqPermission.class).whereById(id).firstOrNull();
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.PermissionService#insert(cn.hg.solon.youcan.system.entity.Permission)
     */
    @Tran
    @Override
    public boolean insert(Permission bean) {
        /*
         * 无父对象ID时，默认为0
         */
        if (ObjUtil.isNull(bean.getParentId())) {
            bean.setParentId(0);
        }

        if (!this.checkUnique(bean)) {
            throw new ServiceException(AppConstants.RETURN_CODE_VALUE_FAILED, "权限名称已经存在，请更换其它值！");
        }

        Permission partner = this.get(bean.getParentId());
        bean.setAncestors(ObjUtil.isNull(partner) ? "0" : partner.getAncestors() + "," + bean.getParentId());

        EqPermission cloneBean = BeanUtil.copyProperties(bean, EqPermission.class);

        return this.easyQuery.insertable(cloneBean).executeRows() > 0;
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.PermissionService#listBy(java.util.Map)
     */
    @Override
    public List<? extends Permission> listBy(Map<String, Object> paraMap) {
        return this.buildQuery(paraMap).toList();
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.PermissionService#listByRole(cn.hg.solon.youcan.system.entity.Role)
     */
    @Override
    public List<? extends Permission> listByRole(Role bean) {
        if (ObjUtil.isNull(bean)) {
            return ListUtil.empty();
        }

        if (bean.getId() == 1) {
            return this.easyQuery.queryable(EqPermission.class)
                .where(w -> w.eq(EqPermission::getStatus, BeanStatus.ON.name()))
                .orderByAsc(o -> o.column(EqPermission::getSort))
                .toList();
        }

        return this.buildQueryByRole(bean).toList();
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.PermissionService#listByUser(cn.hg.solon.youcan.system.entity.User)
     */
    @Override
    public List<? extends Permission> listByUser(User user) {
        if (ObjUtil.isNull(user)) {
            return ListUtil.empty();
        }

        if (user.getId() == 1) {
            return this.easyQuery.queryable(EqPermission.class)
                .where(w -> w.eq(EqPermission::getStatus, BeanStatus.ON.name()))
                .orderByAsc(o -> o.column(EqPermission::getSort))
                .toList();
        }

        return this.buildQueryByUser(user).toList();
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.PermissionService#update(cn.hg.solon.youcan.system.entity.Permission)
     */
    @Tran
    @Override
    public boolean update(Permission bean) {
        /*
         * 无父对象ID时，默认为0
         */
        if (ObjUtil.isNull(bean.getParentId())) {
            bean.setParentId(0);
        }

        if (!this.checkUnique(bean)) {
            throw new ServiceException(AppConstants.RETURN_CODE_VALUE_FAILED, "权限名称已经存在，请更换其它值！");
        }

        Permission partner = this.get(bean.getParentId());
        bean.setAncestors(ObjUtil.isNull(partner) ? "0" : partner.getAncestors() + "," + bean.getParentId());

        EqPermission cloneBean = (EqPermission)this.get(bean.getId());

        BeanUtil.copyProperties(bean, cloneBean, CopyOptions.of().setIgnoreNullValue(true));

        return this.easyQuery.updatable(cloneBean).executeRows() > 0;
    }

}
